package org.lex.reflect.browsing.model;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;


import org.lex.swing.Swings;
import org.lex.utils.BinarySearch;
import org.lex.utils.CategoryComparator;
import org.lex.utils.Comparators;
import org.lex.utils.text.Splitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ClassPathTreeFactory {
	private final static String ROOT_NAME = "Class Path";
	private Logger log = LoggerFactory.getLogger(ClassPathTreeFactory.class);

	public static void main(String[] args) {
		List<ClassPath> users = new ClassPathFactory().loadUserClassPath();
		TreeNode root = new ClassPathTreeFactory().buildClassPaths(users);
		Swings.show(new JScrollPane(new JTree(root, false)));
	}

	public Node buildSystemAndUserClassPaths() {
		List<ClassPath> classPaths = new ClassPathFactory().loadSystemAndUserClassPaths();
		return buildClassPaths(classPaths);
	}

	public Node buildClassPaths(Collection<ClassPath> classPaths) {
		Node root = new Node(ROOT_NAME, Node.Type.ROOT);
		for (ClassPath classPath : classPaths) {
			Node node = buildClassPath(classPath);
			root.insert(node, root.getChildCount());
		}
		return root;
	}

	public Node buildClassPath(ClassPath classPath) {
		Node root = new Node(classPath.getFile(), Node.Type.FOLDER);
		for (String className : classPath.getClassNames()) {
			binaryInsertNode(root, className);
		}
		return root;
	}

	// fullName like: java/lang/String.class
	private void binaryInsertNode(MutableTreeNode root, String fullClassName) {
		String[] path = Splitter.DotSplitter.split(fullClassName);
		if (null == path || 0 == path.length) {
			log.warn("class name=" + fullClassName + " NOT valid");
			return;
		}
		int i = 0;
		MutableTreeNode current = root;
		while (i < path.length) {
			String name = path[i];
			Node node = (Node) findChild(current, name);
			if (null == node) {
				// add "name" to tree;
				Node.Type type = null;
				if (i == path.length - 1) {
					type = Node.Type.CLASS;
				} else {
					type = Node.Type.PACKAGE;
				}
				node = new Node(name, type);
				int pos = BinarySearch.search(current, name);
				if (pos > 0) {
					log.warn("WARNING: " + name + " already exist!");
				}
				if (pos < 0) {
					pos = -pos - 1;
				}
				current.insert(node, pos);
			}
			current = node;
			i++;
		}
	}

	private TreeNode findChild(TreeNode root, String child) {
		int count = root.getChildCount();
		for (int i = 0; i < count; i++) {
			TreeNode node = root.getChildAt(i);
			if (node.toString().equals(child))
				return node;
		}
		return null;
	}

	final static Comparator<TreeNode> cp = Comparators.newComparator(new CategoryComparator<TreeNode>() {
		public int compare(TreeNode n1, TreeNode n2) {
			return n1.toString().compareTo(n2.toString());
		}

		public int getCategory(TreeNode node) {
			if (node.getChildCount() > 0 || node.getAllowsChildren()) {
				return 1;
			} else {
				return -1;
			}
		}
	});

}
